package edu.kit.aifb.ldstream.learner;


import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import edu.kit.aifb.ldstream.stat.util.IDistributionCheckerFactory;

import edu.kit.aifb.ldstream.env.Environment;
import edu.kit.aifb.ldstream.learner.out.xml.ModStatsXMLConverter;

import edu.kit.aifb.ldstream.learner.tasks.mod.CoherenceTask;
import edu.kit.aifb.ldstream.learner.tasks.mod.LearnClasses;
import edu.kit.aifb.ldstream.learner.tasks.mod.ModNumTriplesTask;
import edu.kit.aifb.ldstream.learner.tasks.mod.ModStatistics;
import edu.kit.aifb.ldstream.learner.tasks.mod.PropDistribTask;
import edu.kit.aifb.ldstream.learner.tasks.mod.Type;
import edu.kit.aifb.ldstream.learner.tasks.mod.ValueDistrib;

public class Learner {


	private ExecutorService threadPool;
	private IDistributionCheckerFactory distribCheckerFactory;
	private ModStatistics stats;

	private boolean inferencing = Environment.INFERENCING;

	public Learner(ExecutorService threadPool, 
			IDistributionCheckerFactory factory,
			ModStatistics stats) {
		this.distribCheckerFactory = factory;
		this.threadPool = threadPool;
		this.stats = stats;

	}

	public void learn() {

		if(Environment.VERBOSE){
			System.out.println("Started the learning process.");
		}
		ModNumTriplesTask mntt = new ModNumTriplesTask(this.stats);

		Set<Type> typeSet = new TreeSet<Type>();
		LearnClasses lc = new LearnClasses(typeSet, stats, this.distribCheckerFactory.getNewDistributionChecker());

		Future<?> mnttFuture = this.threadPool.submit(mntt);


		try {
			mnttFuture.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		mntt = null;
		mnttFuture = null;

		Future<?> lcFuture = this.threadPool.submit(lc);

		try {
			lcFuture.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}



		lc = null;
		lcFuture = null;
		System.gc();

		if(!Environment.IO_DEGREE){

			PropDistribTask pdt = new PropDistribTask(typeSet, this.inferencing, this.distribCheckerFactory.getLastInstance(), this.stats);
			Future<?> pdtFuture = this.threadPool.submit(pdt);

			try {
				pdtFuture.get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
			pdt = null;
			pdtFuture = null;
			System.gc();

		}

		if(!Environment.IO_DEGREE){
			ValueDistrib vdt = new ValueDistrib(inferencing, typeSet, this.distribCheckerFactory.getLastInstance() );
			Future<?> vdtFuture = this.threadPool.submit(vdt);

			try {
				vdtFuture.get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}

			System.gc();
		}


		CoherenceTask ch = new CoherenceTask(this.stats, typeSet);
		Future<?> chFuture = this.threadPool.submit(ch);

		try {
			chFuture.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

		ModStatsXMLConverter msc = new ModStatsXMLConverter();
		msc.toXML(this.stats);

		/*
		 * Execution order according to the task dependencies
		 * 
		 * 	NumTriples							
		 * 	LearnClasses	=>	LearnInstances	=>	valueDistribution	
		 * 					=>	LearnInstances	=>	structuredness
		 * 						LearnInstances	=>	propertyDistribution
		 * 						LearnInstances	=>	Outdegree
		 * 						LearnInstances	=>	Indegree
		 */
		/*
		LearnerTask numTriples = new NumTriplesTask(simpleStats);
//		LearnerTask learnClasses = new LearnClassesTask(simpleStats, this.classMap);
		LearnerTask learnInstances = new LearnInstancesTask(this.threadPool, this.classMap, this.simpleStats, 
																this.distribCheckerFactory.getLastInstance(), this.inferencing, true);
		Coherence ch = new Coherence();
		LearnerTask structuredness = new StructurednessTask(classMap, ch, this.inferencing);
		LearnerTask indegree = new IndegreeTask(this.simpleStats, this.classMap, this.distribCheckerFactory.getNewDistributionChecker());
		LearnerTask outdegree = new OutdegreeTask(this.simpleStats, this.classMap, this.distribCheckerFactory.getNewDistributionChecker());
		LearnerTask properties = new PropertyDistributionTask(classMap, this.propertyResultMap, this.inferencing, this.distribCheckerFactory.getNewDistributionChecker());
		LearnerTask valueDistribution = new ValueDistributionTask(this.classMap, this.valueResultMap, 
																	this.distribCheckerFactory.getNewDistributionChecker());

		Future<?>[] futures = new Future<?>[7];

		futures[0] = this.threadPool.submit(numTriples);
		 */
		/*		futures[1] = this.threadPool.submit(learnClasses);

		try{
			futures[1].get();
		} catch (InterruptedException | ExecutionException e1) {
			e1.printStackTrace();
		}
		 */
		/*
		futures[1] = this.threadPool.submit(learnInstances);
		try {
			try {
				futures[1].get();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
		futures[2] = this.threadPool.submit(outdegree);
		futures[3] = this.threadPool.submit(indegree);
		futures[4] = this.threadPool.submit(structuredness);
		futures[5] = this.threadPool.submit(valueDistribution);
		futures[6] = this.threadPool.submit(properties);

		for(Future<?> f : futures){
			try {
				f.get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
		this.simpleStats.setStructuredness(ch.getCoherence());
		System.out.println("Coverage " + ch.getCoherence());
		overAllLinkageDegree();
		if(Environment.VERBOSE){
			System.out.println("Learning process finished");
		}

		if(Environment.VERBOSE){
			System.out.println("Saving the Learning results.");
		}

		storeAsXML();
		 */
		/*
		storeResultToFile(this.propertyResultMap, Environment.PROP_RESULT_FILE_NAME);
		storeResultToFile(this.valueResultMap, Environment.VALUE_RESULT_FILE_NAME);
		storeResultToFile(this.simpleStats, Environment.SIMPLE_STATS_RESULT_FILE_NAME);
		storeResultToFile(this.classMap, Environment.CLASS_MAP_RESULT_FILE_NAME);
		 */ 
		if(Environment.VERBOSE){
			System.out.println("Learning results saved.");
		}


	}
	/*
	private void overAllLinkageDegree(){
		double degree = 0d;
		for(Type t : this.classMap.keySet()){
			degree += t.getLinkageDegree();
		}
		this.simpleStats.setLinkageDegree(degree / this.classMap.size());  
	}

	private void storeResultToFile(Object result, String fileName){

		File outputFile = new File(Environment.DEFAULT_OUTPUT_FILE_LOCATION + fileName);
		if(outputFile.exists()){
			// delete the old file
			outputFile.delete();
		}
		boolean created;
		try {
			outputFile.createNewFile();
			created = true;
		} catch (IOException e) {
			System.err.println("Could not create output file");
			created = false;
		}
		if(created){
			FileOutputStream fos = null;
			ObjectOutputStream oos = null;
			try {
				fos = new FileOutputStream(outputFile);
				oos = new ObjectOutputStream(fos);
				oos.writeObject(result);

			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void storeAsXML(){

		SimpleStatsXMLConverter ssxc = new SimpleStatsXMLConverter();
		ssxc.toXML(this.simpleStats);
		ssxc = null;

		ClassMapXMLConverter cmxc = new ClassMapXMLConverter();
		cmxc.toXML(this.classMap);
		cmxc = null;

		PropertyResultMapXMLConverter prmxc = new PropertyResultMapXMLConverter();
		prmxc.toXML(this.propertyResultMap);
		prmxc = null;

		ValueResultMapXMLConverter vrmxc = new ValueResultMapXMLConverter(); 
		vrmxc.toXML(this.valueResultMap);
		vrmxc = null;
	}
	 */
}
